<?php

use think\Facade\Cache;

define('E_OK', 0);
define('E_EXCEPTION', -500);
define('E_INTERNAL', -501);

define("E_PARAMS_ERROR", -502);
define("E_ENC_ERROR", -503);
define('E_TOKEN_ERROR', -530);

define('X_ERROR', -1);

class RequestContext
{
    static $opid;
    static $debug = true;
    static $internal = false;
    static $client_ip;
    static $sn;
    static $post_data_raw;
    static $response_raw;
    static $ok = false;
}


const MODULE_USER = 'USER';
const MODULE_USER_WALLET = 'USER_WALLET';
const MODULE_MODE_SELF_READ = 1;
const MODULE_MODE_CITY_READ = 2;
const MODULE_MODE_GLOBAL_READ = 4;
const MODULE_MODE_SELF_WRITE = 1;
const MODULE_MODE_CITY_WRITE = 2;
const MODULE_MODE_GLOBAL_WRITE = 4;


const OPER_TYPE_USER = 0;
const OPER_TYPE_CC = 1;
const OPER_TYPE_OPS = 2;
const OPER_TYPE_CS = 3;
const OPER_TYPE_SYSTEM = 99;

//公共函数

//---------------
function redis_handler()
{
    return Cache::store('redis')->handler();
}
//----------------------------


class ApiOper
{

    var $_user_info = null;
    var $_token = null;

    function userinfo()
    {
        return $this->_user_info;
    }

    static function instance()
    {
        static $ins = null;
        if (empty($ins)) {
            $ins = new ApiOper();
        }
        return $ins;
    }

    function append_updater_info(&$info)
    {
        if (!empty($this->_user_info)) {
            $info['updater_id'] = $this->_user_info['id'];
            $info['updater_name'] = $this->_user_info['name'];
        }
        $info['update_time'] = date('Y-m-d H:i:s');
    }

    function append_creater_info(&$info)
    {
        if (!empty($this->_user_info)) {
            $info['creator_id'] = $this->_user_info['id'];
            $info['creator_name'] = $this->_user_info['name'];
            $info['create_time'] = date('Y-m-d H:i:s');
        }
    }

    function get_token()
    {
        return $this->_token;
    }

    function set_token($token)
    {
        $this->_token = $token;
    }

}

class MyException extends Exception {
    public function __construct($code, $message) {
        parent::__construct($message, $code);
    }
}
function throw_myexception($code, $msg = '') {
    throw new MyException($code, $msg);
}

function error_msg($code) {
    $errortable = array(
        E_EXCEPTION => "内部异常",
        E_INTERNAL => "内部错误",
        E_TOKEN_ERROR => 'token 错误',
        E_PARAMS_ERROR => '参数错误',
        X_ERROR => '业务错误',
    );

    if (array_key_exists($code, $errortable)) {
        return $errortable[$code];
    }

    return 'undefined error';
}

class vtimer
{

    static function instance()
    {
        static $ins = null;
        if (empty($ins)) {
            $ins = new vtimer();
        }
        return $ins;
    }

    var $_start_ms;

    public function __construct()
    {
        $this->_start_ms = common::time_ms();
    }

    public function cost()
    {
        $now = common::time_ms();
        return $now - $this->_start_ms;
    }

}

class common
{

    static function time_ms()
    {
        list($microsecond, $time) = explode(' ', microtime()); //' '中间是一个空格
        return (float)sprintf('%.0f', (floatval($microsecond) + floatval($time)) * 1000);
    }

    static function time_ns()
    {
        list($microsecond, $time) = explode(' ', microtime()); //' '中间是一个空格

        $microsecond = intval($microsecond * 1000000);
        return $time . str_pad($microsecond, '0', STR_PAD_LEFT) . '000';
    }

    static function udate()
    {
        list($microsecond, $time) = explode(' ', microtime());

        $microsecond = floor($microsecond * 1000000);
        $microsecond = str_pad($microsecond, 6, '0', STR_PAD_LEFT);
        $s = date('ymdHis', $time) . $microsecond;
        return $s;
    }

    //取字符串整数
    static function ver_str_to_int($version)
    {
        $iv = 0;
        $ret = explode('.', $version);

        if ($ret > 0) {
            for ($i = 0; $i < count($ret); $i++) {
                $iv = $iv + $ret[$i] * pow(10, 6 - $i * 3);
            }
        }

        return $iv;
    }

    //去除空格和回车符
    static function trim($s)
    {
        //return $s;
        $message = trim($s);
        $message = str_replace("\r\n", " ", $message);
        $message = str_replace("\r", " ", $message);
        $message = str_replace("\n", " ", $message);
        $message = str_replace("\t", " ", $message);
        $message = str_replace("\x0B", " ", $message);
        return $message;
        // return trim($message, '\r\n\t\x0B');
    }

    static function filter_space(&$s)
    {
        $s = str_replace(' ', '', $s);
    }

    //是否是日期
    static function is_date($date)
    {
        if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) {
            if (checkdate($matches[2], $matches[3], $matches[1])) {
                return true;
            }
        }
        return false;
    }

    //是否是一个日期带时间的
    static function is_datetime($dateTime)
    {
        if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) {
            //debuginfo("is_datetime preg matches:".json_encode($matches));
            if (checkdate($matches[2], $matches[3], $matches[1])) {
                return true;
            }
        }

        return false;
    }


    //是否是一个12:59:59类似的时间
    static function is_time($time)
    {
        if (preg_match("/^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $time, $matches)) {
            return true;
        }

        return false;
    }

    //按格式转换为数组
    static function type_conver($jsdata, $fields)
    {
        $out = [];
        foreach ($jsdata as $key => $val) {
            if (array_key_exists($key, $fields)) {
                if ($fields[$key] == 'int') {
                    $out[$key] = (int)$val;
                } else if ($fields[$key] == 'string') {
                    if (is_array($val) || is_object($val)) {
                        $out[$key] = json_encode($val, true);
                    } else if (is_numeric($val)) {
                        $out[$key] = strval($val);
                    } else {
                        $out[$key] = $val;
                    }
                } else if ($fields[$key] == 'object' || $fields[$key] == 'array') {
                    if (is_array($val) || is_object($val)) {
                        $out[$key] = $val;
                    } else {
                        $out[$key] = json_decode($val, true);
                    }
                } else {
                    $out[$key] = $val;
                }
            }
        }

        return $out;
    }

    static function check_json_keys($jsdata, $fields, &$what)
    {
        $data = array();
        foreach ($fields as $key => $o) {
            $type = null;
            $def = null;
            if (is_string($o)) {
                $type = $o;
            } else {
                $type = $o[0];
                $def = $o[1];
            }
            if (!array_key_exists($key, $jsdata)) {
                if (preg_match("/^opt/", $type)) {
                    $data[$key] = $def;
                    continue;
                } else {
                    $what = $key . " miss ";
                    return false;
                }
            } else {
                $val = $jsdata[$key];
                $match = false;
                do {
                    if (preg_match('/string$/', $type)) {
                        if (!is_string($val))
                            break;
                    } else if (preg_match('/phone$/', $type)) {
                        if (!is_string($val))
                            break;

                        if (!common::is_phone($val)) {
                            break;
                        }
                    } else if (preg_match('/idcno$/', $type)) {
                        if (!is_string($val))
                            break;

                        if (!common::is_idcard($val)) {
                            break;
                        }
                    } else if (preg_match('/cnname/', $type)) {
                        if (!is_string($val))
                            break;

                        if (!common::is_name($val)) {
                            break;
                        }
                    } else if (preg_match('/int$/', $type)) {
                        if (!is_int($val))
                            break;
                    } else if (preg_match('/float$/', $type)) {
                        if (!is_float($val))
                            break;
                    } else if (preg_match('/bool$/', $type)) {
                        if (!is_bool($val))
                            break;
                    } else if (preg_match('/date$/', $type)) {
                        if (!common::is_date($val))
                            break;
                    } else if (preg_match('/datetime$/', $type)) {
                        if (!common::is_datetime($val))
                            break;
                    } else if (preg_match('/time$/', $type)) {
                        if (!common::is_time($val))
                            break;
                    } else if (preg_match('/array$/', $type)) {
                        if (!is_array($val))
                            break;
                    } else if (preg_match('/intarray$/', $type)) {
                        if (!is_array($val))
                            break;
                        foreach ($val as $sv) {
                            if (!is_int($sv))
                                break;
                        }
                    } else if (preg_match('/floatarray$/', $type)) {
                        if (!is_array($val))
                            break;
                        foreach ($val as $sv) {
                            if (!is_float($sv))
                                break;
                        }
                    } else if (preg_match('/stringarray$/', $type)) {
                        if (!is_array($val))
                            break;
                        foreach ($val as $sv) {
                            if (!is_string($sv) || empty($sv))
                                break;
                        }
                    } else if (preg_match('/polygon$/', $type)) {
                        if (!common::is_polygon($val)) {
                            break;
                        }
                    }
                    $match = true;
                } while (0);

                if (!$match) {
                    $what = "$key must be $type ";
                    return null;
                }
                $data[$key] = $val;
            }
        }

        return $data;
    }

    static public function check_json_optional($jsdata, $fields)
    {
        $out = array();
        foreach ($fields as $key => $type) {
            if (!array_key_exists($key, $jsdata)) {
                continue;
            }
            if (($type == 'string' && is_string($jsdata[$key])) || ($type == 'int' && is_int($jsdata[$key]))) {
                $out[$key] = $jsdata[$key];
            }
        }
        return $out;
    }

    static public function array_keys($arr)
    {
        $keys = array();
        foreach ($arr as $key => $val) {
            $keys[] = $key;
        }
        sort($keys);
        return $keys;
    }

    static public function array_vals($arr)
    {
        $vals = array();
        foreach ($arr as $key => $val) {
            $vals[] = $val;
        }
        return $vals;
    }

    static public function sql_cond_equal($arr)
    {
        $s = "";
        foreach ($arr as $key => $val) {
            if (is_int($val)) {
                $s = $s . " and $key = $val";
            } else if (is_string($val)) {
                $v = hisql::db()->escapeString($val);
                $s = $s . " and $key = $v";
            }
        }
        return $s;
    }

    static public function sql_cond_like($arr)
    {
        $s = "";
        foreach ($arr as $key => $val) {
            if (is_string($val)) {
                $ess = addslashes($val);
                $s = $s . " and $key like '%$ess%'";
            }
        }
        return $s;
    }

    static public function stupid($prefix = '')
    {
        return uniqid($prefix);
    }

    function min_multiple($a, $b)
    {
        if ($b == 0) {     //一定要考虑除数不能为零
            return $b;
        } else {
            $m = max($a, $b);
            $n = min($a, $b);
            for ($i = 2; ; $i++) {
                if (is_int($m * $i / $n)) {
                    return $i;
                }
            }
        }
        return $a * $b;
    }

    static function gcd($m, $n)
    {
        return ($m % $n == 0) ? $n : common::gcd($n, $m % $n);
    }

    static function lcm($a, $b)
    {
        return $a * $b / common::gcd($a, $b);
    }

    static public function merge_time_points($mins1, $points1, $mins2, $kv)
    {
        $gcd = common::gcd($mins1, $mins2);
        $time = $mins1 / $gcd;
        $tmppoints = [];
        for ($i = 0; $i < intval(60 * 24 / $mins1); $i++) {
            for ($k = 0; $k < $time; $k++) {
                if (isset($points1[$i]))
                    $tmppoints[$i * $time + $k] = $points1[$i];
                else {
                    $tmppoints[$i * $time + $k] = 0;
                }
            }
        }

        $time = $mins2 / $gcd;

        $tmpkv = [];
        foreach ($kv as $k => $v) {
            for ($i = 0; $i < $time; $i++) {
                $idx = $k * $time + $i;
                if ($idx < (60 * 24 / $gcd)) {
                    $tmpkv[$idx] = $v;
                }
            }
        }
        foreach ($tmpkv as $k => $v) {
            $tmppoints[$k] = $v;
        }
        $result['mins'] = $gcd;
        $result['points'] = $tmppoints;
        return $result;
    }

    static function curlpost($url, $postdata, $headers)
    {

        $start_time = explode(" ", microtime());
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $hs = [
            'X-ptype: like me',
            'Expect: ',
        ];
        if (!empty($headers)) {
            $hs = array_merge($hs, $headers);
        }

        curl_setopt($ch, CURLOPT_HTTPHEADER, $hs);

        curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
        $content = curl_exec($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $pos = strpos($url, 'https');
        if ($pos !== FALSE) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        }
        curl_close($ch);
        $end_time = explode(" ", microtime());

        $cost = intval(($end_time[0] + $end_time[1] - $start_time[1] - $start_time[0]) * 1000);

        if ($code != 200) {
            api('log')->error("curlpost: [$url][cost: $cost], [postdata:$postdata], [httpcode:$code] [content:$content]");
            return null;
        }

        api('log')->debug("curlpost: [$url][cost: $cost], [postdata:$postdata], [httpcode:$code] [content:$content]");
        return $content;
    }

    static function curlpostjson($url, $params, $headers = [])
    {
        $postdata = json_encode($params);
        $headers = array_merge($headers, [
            'Content-Type: application/json',
        ]);
        $content = common::curlpost($url, $postdata, $headers);
        if (!$content) {
            return false;
        }
        $json = json_decode($content, true);
        if (!$json) {
            return false;
        }

        return $json;
    }

    static function curlgetjson($url, $params, $headers = [])
    {
        $content = common::curlget($url, $params, $headers);
        if (!$content) {
            return false;
        }

        $json = json_decode($content, true);
        if (!$json) {
            return false;
        }

        return $json;
    }

    static function curlget($url, $params, $headers = [])
    {
        $start_time = explode(" ", microtime());
        $ch = curl_init();
        $querys = null;
        if (!empty($params)) {
            $querys = [];
            foreach ($params as $k => $v) {
                $querys[] = "$k=" . rawurlencode($v);
            }
            $querys = implode('&', $querys);
            $url = $url . "?" . $querys;
        }
        $hs = [
            'X-ptype: like me',
            'Expect: ',
        ];
        if (!empty($headers)) {
            $hs = array_merge($hs, $headers);
        }

        curl_setopt($ch, CURLOPT_HTTPHEADER, $hs);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);

        $content = curl_exec($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);


        $end_time = explode(" ", microtime());
        $cost = intval(($end_time[0] + $end_time[1] - $start_time[1] - $start_time[0]) * 1000);
        if ($code != 200) {
            app('log')->debug("curlget: [$url] [cost:$cost] [querydata:$querys], [httpcode:$code] [content:$content]");
            return null;
        }

        app('log')->debug("curlget: [$url] [cost:$cost] [querydata:$querys], [httpcode:$code] [content:$content]");

        return $content;
    }


    static function sn()
    {
        return sprintf('%04x%04x-%04x%04x-%04x%04x-%04x%04x',
            // 32 bits for "time_low"
            mt_rand(0, 0xffff), mt_rand(0, 0xffff),
            // 16 bits for "time_mid"
            mt_rand(0, 0xffff),
            // 16 bits for "time_hi_and_version",
            // four most significant bits holds version number 4
            mt_rand(0, 0x0fff) | 0x4000,
            // 16 bits, 8 bits for "clk_seq_hi_res",
            // 8 bits for "clk_seq_low",
            // two most significant bits holds zero and one for variant DCE1.1
            mt_rand(0, 0x3fff) | 0x8000,
            // 48 bits for "node"
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
        );
    }

    static function sql_row($selectlist, $row)
    {
        $ret = [];
        foreach ($selectlist as $key => $type) {
            if (array_key_exists($key, $row)) {
                if ($row[$key] !== null) {
                    if ($type == 'int') {
                        $ret[$key] = intval($row[$key]);
                    } else if ($type == 'array' || $type == 'object') {
                        $ret[$key] = json_decode($row[$key], TRUE);
                    } else {
                        $ret[$key] = $row[$key];
                    }
                } else {
                    $ret[$key] = null;
                }
            }
        }
        return $ret;
    }

    //array hash
    static function array_hash($array, $hkey)
    {
        $count = count($array);
        $index = hexdec(substr(md5($hkey), 0, 4)) % $count;
        return $array[$index];
    }

    static function hash_hkey($hkey, $count)
    {
        return hexdec(substr(md5($hkey), 0, 4)) % $count;
    }

    static function array_magnify($array, $multiple)
    {
        if (empty($array)) {
            $array = [0];
        }
        $length = count($array);
        if ($multiple > 1) {
            $tmppoints = [];
            for ($i = 0; $i < $length; $i++) {
                for ($k = 0; $k < $multiple; $k++) {
                    if (isset($array[$i]))
                        $tmppoints[$i * $multiple + $k] = $array[$i];
                    else {
                        $tmppoints[$i * $multiple + $k] = 0;
                    }
                }
            }
            $array = $tmppoints;
        }
        return $array;
    }

    static function firstname($fullname)
    {
        $pos = strpos($fullname, ' ');

        if ($pos == false) {
            return mb_substr($fullname, 0, 1);
        }

        return substr($fullname, 0, $pos);
    }

    static function object_array_sort_by_key($array, $key, $flag = SORT_REGULAR)
    {
        $m = [];
        foreach ($array as $e) {
            $m[$e[$key]] = $e;
        }

        ksort($m, $flag);
        $m = array_values($m);
        return $m;
    }

    static function print_stack_trace()
    {
        $array = debug_backtrace();
        $html = '';
        unset($array[0]);
        foreach ($array as $row) {
            $html .= $row['file'] . ':' . $row['line'] . '行,调用方法:' . $row['function'] . "\n";
        }
        return $html;
    }

    static function int_map($keys, $default = 0)
    {
        $array = [];
        foreach ($keys as $key) {
            $array[$key] = $default;
        }
        return $array;
    }

    static function complete_map($map, $keys, $default)
    {
        $m = [];
        if (!empty($map))
            $m = $map;
        foreach ($keys as $key) {
            if (!isset($m[$key])) {
                $m[$key] = $default;
            }
        }
        return $m;
    }

    static function array_map($keys)
    {
        $array = [];
        foreach ($keys as $key) {
            $array[$key] = [];
        }
        return $array;
    }

    static function safe_index($m, $k)
    {
        if (isset($m[$k]))
            return $m[$k];

        return null;
    }

    static function utf8_to_unicode_str($utf8)
    {
        $return = '';

        for ($i = 0; $i < mb_strlen($utf8); $i++) {

            $char = mb_substr($utf8, $i, 1);

            // 3字节是汉字，不转换，4字节才是 emoji
            if (strlen($char) > 3) {
                $char = trim(json_encode($char), '"');
            }
            $return .= $char;
        }
        return $return;
    }

    static function cards_sub($a, $b)
    {
        $aaa = $a;
        $count_a = count($a);
        $count_b = count($b);
        foreach ($b as $cb) {
            for ($i = 0; $i < count($a); $i++) {
                if ($cb[0] == $a[$i][0] && $cb[1] == $a[$i][1]) {
                    array_splice($a, $i, 1);
                    break;
                }
            }
        }

        $count_left = count($a);

        if ($count_b + $count_left != $count_a) {
            return null;
        }
        return $a;
    }

    static function combination($a, $m)
    {
        $r = array();

        $n = count($a);
        if ($m <= 0 || $m > $n) {
            return $r;
        }

        for ($i = 0; $i < $n; $i++) {
            $t = array($a[$i]);
            if ($m == 1) {
                $r[] = $t;
            } else {
                $b = array_slice($a, $i + 1);
                $c = self::combination($b, $m - 1);
                foreach ($c as $v) {
                    $r[] = array_merge($t, $v);
                }
            }
        }

        return $r;
    }

    static function array_to_xml($data)
    {
        if (!is_array($data) || count($data) <= 0) {
            return false;
        }
        $xml = "<xml>";
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    static function xml_to_array($xml)
    {
        libxml_disable_entity_loader(true);
        $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $result;
    }

    static function is_idcard($id)
    {
        $id = strtoupper($id);
        $regx = "/(^\d{15}$)|(^\d{17}([0-9]|X)$)/";
        $arr_split = array();
        if (!preg_match($regx, $id)) {
            return FALSE;
        }
        if (15 == strlen($id)) { //检查15位
            $regx = "/^(\d{6})+(\d{2})+(\d{2})+(\d{2})+(\d{3})$/";

            @preg_match($regx, $id, $arr_split);
            //检查生日日期是否正确
            $dtm_birth = "19" . $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
            if (!strtotime($dtm_birth)) {
                return FALSE;
            } else {
                return TRUE;
            }
        } else {      //检查18位
            $regx = "/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/";
            @preg_match($regx, $id, $arr_split);
            $dtm_birth = $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
            if (!strtotime($dtm_birth)) { //检查生日日期是否正确
                return FALSE;
            } else {
                //检验18位身份证的校验码是否正确。
                //校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
                $arr_int = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
                $arr_ch = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
                $sign = 0;
                for ($i = 0; $i < 17; $i++) {
                    $b = (int)$id{$i};
                    $w = $arr_int[$i];
                    $sign += $b * $w;
                }
                $n = $sign % 11;
                $val_num = $arr_ch[$n];
                if ($val_num != substr($id, 17, 1)) {
                    return FALSE;
                } //phpfensi.com
                else {
                    return TRUE;
                }
            }
        }
    }

    static function is_phone($text)
    {
        return preg_match("/^1[23456789]\d{9}$/", $text);
    }

    static function is_name($text)
    {
        if (preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', $text) > 0) {
            return true;
        }
        return false;
    }

    static function is_user_id($s)
    {
        return false;
    }

    static function is_bike_id($s)
    {

    }

    static function is_staff_id($s)
    {

    }

    static function is_warehouse_id($s)
    {

    }

    /**
     * 求两个已知经纬度之间的距离,单位为米
     *
     * @param lng1 $ ,lng2 经度
     * @param lat1 $ ,lat2 纬度
     * @return float 距离，单位米
     * @author www.Alixixi.com
     */
    static function distance($coordinate1, $coordinate2)
    {
        list($lng1, $lat1) = $coordinate1;
        list($lng2, $lat2) = $coordinate2;
        // 将角度转为狐度
        $radLat1 = deg2rad($lat1); //deg2rad()函数将角度转换为弧度
        $radLat2 = deg2rad($lat2);
        $radLng1 = deg2rad($lng1);
        $radLng2 = deg2rad($lng2);
        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;
        $s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))) * 6378.137 * 1000;
        $s = floor($s);
        return $s;
    }

    static function circle_to_polygon($coord, $radius)
    {
        $r2 = $radius * $radius;
        $coords = [];
        $d = 4;
        $coords[] = [$coord[0] - $radius, $coord[1]];
        for ($i = 1 - $d; $i < $d; $i++) {
            $x = $coord[0] + $i / $d * $radius;
            $y = $coord[1] + sqrt($r2 - ($i * $i / $d / $d * $radius * $radius));
            $coords[] = [$x, $y];
        }

        $coords[] = [$coord[0] + $radius, $coord[1]];
        for ($i = $d - 1; $i > 0 - $d; $i--) {
            $x = $coord[0] + $i / $d * $radius;
            $y = $coord[1] - sqrt($r2 - ($i * $i / $d / $d * $radius * $radius));
            $coords[] = [$x, $y];
        }
        $coords[] = [$coord[0] - $radius, $coord[1]];
        return $coords;
    }

    //获取多边形的中心
    static function polygon_center($coords)
    {
        //$coords = [[119.281444, 26.065064], [119.289426, 26.062636], [119.289469, 26.051764], [119.284963, 26.055812], [119.280714, 26.056853], [119.277152, 26.058626], [119.275049, 26.061672], [119.281444, 26.065064]];
        // $coords = [[0, 0], [100, 0], [200, 200], [0, 100], [0, 0]];
        $n = 20;
        while (--$n > 0) {
            $coords_new = [
            ];
            for ($i = 0; $i < count($coords) - 1; $i++) {
                $coords_new[] = [
                    ($coords[$i][0] + $coords[$i + 1][0]) / 2,
                    ($coords[$i][1] + $coords[$i + 1][1]) / 2,
                ];
            }
            $coords_new[] = $coords_new[0];
            $coords = $coords_new;
        }

        $sanjiaoxing = [
            $coords[0],
            $coords[floor(count($coords) / 2)],
            $coords[count($coords) - 1],
        ];

        $line = [
            $sanjiaoxing[0],
            [
                ($sanjiaoxing[1][0] + $sanjiaoxing[2][0]) / 2,
                ($sanjiaoxing[1][1] + $sanjiaoxing[2][1]) / 2,
            ]
        ];

        $point = [
            ($line[0][0] + $line[1][0]) / 2,
            ($line[0][1] + $line[1][1]) / 2,
        ];
        return $point;
    }

    static function random_string($len, $special = false)
    {
        $chars = array("A", "B", "C", "D", "E", "F", "G",
            "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2",
            "3", "4", "5", "6", "7", "8", "9"
        );
        if ($special) {
            $chars = array_merge($chars, array(
                "!", "@", "#", "$", "?", "|", "{", "/", ":", ";",
                "%", "^", "&", "*", "(", ")", "-", "_", "[", "]",
                "}", "<", ">", "~", "+", "=", ",", "."
            ));
        }

        $charsLen = count($chars) - 1;
        shuffle($chars);                            //打乱数组顺序
        $str = '';
        for ($i = 0; $i < $len; $i++) {
            $str .= $chars[mt_rand(0, $charsLen)];    //随机取出一位
        }
        return $str;
    }

    static function list_to_tree($list, &$parent)
    {
        $new_list = [];
        foreach ($list as $item) {
            if ($item['parent'] == $parent['id']) {
                $item['children'] = [];
                $parent['children'][] = $item;
            } else {
                $new_list[] = $item;
            }
        }
        $parent['count'] = count($parent['children']);
        foreach ($parent['children'] as &$item) {
            self::list_to_tree($new_list, $item);
        }
    }

    static function decst($num)
    {
        $to = 36;
        $dict = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $ret = '';
        do {
            $ret = $dict[bcmod($num, $to)] . $ret; //bcmod取得高精确度数字的余数。
            $num = bcdiv($num, $to);  //bcdiv将二个高精确度数字相除。
        } while ($num > 0);
        return $ret;
    }

    static function stdec($num)
    {
        $from = 36;
        $num = strval($num);
        $dict = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $len = strlen($num);
        $dec = 0;
        for ($i = 0; $i < $len; $i++) {
            $pos = strpos($dict, $num[$i]);
            $dec = bcadd(bcmul(bcpow($from, $len - $i - 1), $pos), $dec);
        }
        return $dec;
    }

    static function line_cross($a, $b)
    {
        $asx = $a[0][0];
        $asy = $a[0][1];
        $aex = $a[1][0];
        $aey = $a[1][1];
        $bsx = $b[0][0];
        $bsy = $b[0][1];
        $bex = $b[1][0];
        $bey = $b[1][1];

        if (max($asx, $aex) < min($bsx, $bex)) {
            return false;
        }

        if (min($asx, $aex) > max($bsx, $bex)) {
            return false;
        }

        if (max($asy, $aey) < min($bsy, $bey)) {
            return false;
        }

        if (min($asy, $aey) > max($bsy, $bey)) {
            return false;
        }
        if (((($a[0][0] - $b[0][0]) * ($b[1][1] - $b[0][1]) - ($a[0][1] - $b[0][1]) * ($b[1][0] - $b[0][0])) *
                (($a[1][0] - $b[0][0]) * ($b[1][1] - $b[0][1]) - ($a[1][1] - $b[0][1]) * ($b[1][0] - $b[0][0]))) > 0 ||
            ((($b[0][0] - $a[0][0]) * ($a[1][1] - $a[0][1]) - ($b[0][1] - $a[0][1]) * ($a[1][0] - $a[0][0])) *
                (($b[1][0] - $a[0][0]) * ($a[1][1] - $a[0][1]) - ($b[1][1] - $a[0][1]) * ($a[1][0] - $a[0][0]))) > 0) {
            return false;
        }

        return true;
    }

    static function is_polygon($coordinates)
    {
        if (!is_array($coordinates)) {
            return false;
        }
        if (count($coordinates) < 3) {
            return false;
        }

        foreach ($coordinates as $coord) {
            if (!is_array($coord) || count($coord) != 2) {
                return false;
            }
        }
        $count = count($coordinates);
        if ($coordinates[0][0] != $coordinates[$count - 1][0] || $coordinates[0][1] != $coordinates[$count - 1][1]) {
            return false;
        }


        $lines = [];
        for ($i = 0; $i < count($coordinates) - 1; $i++) {
            $lines[] = [$coordinates[$i], $coordinates[$i + 1]];
        }

        for ($i = 0; $i < count($lines); $i++) {
            $a = $lines[$i];
            for ($j = $i + 2; $j < count($lines); $j++) {
                if ($i == 0 && $j == count($lines) - 1) {
                    continue;
                }

                if (common::line_cross($a, $lines[$j])) {
                    return false;
                }
            }
        }

        /*
          for ($i = 0; $i < $count - 2; $i++) {
          $a = [$coordinates[$i], $coordinates[$i + 1]];
          for ($j = $i + 2; $j < $count - 1 && $i + $j < $count - 2; $j++) {
          $b = [$coordinates[$j], $coordinates[$j + 1]];

          if (self::line_cross($a, $b)) {
          hilog::debug("line cross", $a, $b);
          return false;
          }
          }
          } */

        return true;
    }

    static function object_to_array($array)
    {
        if (is_object($array)) {
            $array = (array)$array;
        }
        if (is_array($array)) {
            foreach ($array as $key => $value) {
                $array[$key] = self::object_to_array($value);
            }
        }
        return $array;
    }

    static function get_age_from_idcno($idcno)
    {
        if (!self::is_idcard($idcno)) {
            return 0;
        }

        $birthday = null;
        if (strlen($idcno)) {
            $birthday = substr($idcno, 6, 8);
        } else {
            $birthday = '19' . strstr($idcno, 6, 6);
        }
        $nowdate = date('Ymd');
        $age = floor((intval($nowdate) / 10000) - floor(intval($birthday)) / 10000);
        if (intval($birthday) % 10000 > intval($nowdate) % 10000) {
            //$age--;
        }
        return $age;
    }

    static function get_info_from_idcno($idcno)
    {
        if (!self::is_idcard($idcno)) {
            return null;
        }
        $birthday = null;
        if (strlen($idcno)) {
            $birthday = substr($idcno, 6, 8);
        } else {
            $birthday = '19' . substr($idcno, 6, 6);
        }
        $nowdate = date('Ymd');
        $age = floor((intval($nowdate) / 10000) - floor(intval($birthday)) / 10000);
        if (intval($birthday) % 10000 > intval($nowdate) % 10000) {
            $age--;
        }

        $gender = 0;
        if (strlen($idcno) == 18) {
            $gender = intval(substr($idcno, 16, 1)) % 2;
        } else if (strlen($idcno) == 15) {
            $gender = intval(substr($idcno, 14, 1)) % 2;
        }
        return [
            'birthday' => substr($birthday, 0, 4) . '-' . substr($birthday, 4, 2) . '-' . substr($birthday, 6, 2),
            'age' => $age,
            'gender' => $gender,
        ];
    }

    static function is_lan_ip($ip)
    {
        return preg_match('%^127\.|^10\.|^192\.168|^172\.(1[6-9]|2|3[01])%', $ip);
    }

//暂时这样，转换平台支付
    static function ChannelVal($channel)
    {
        if ($channel == 'wxmpu') {
            return 0;
        } else if ($channel == 'wxapp') {
            return 1;
        } else {
            return 0;
        }
    }

    static function ValToChannel($val)
    {
        switch ($val) {
            case 0:
                return 'wxmpu';
                break;
            case 1:
                return 'wxapp';
                break;
            default:
                return 'wxmpu';
        }
    }

    static function hex2float($strHex)
    {
        $hex = sscanf($strHex, "%02x%02x%02x%02x%02x%02x%02x%02x");
        $bin = implode('', array_map('chr', $hex));
        $array = unpack("f", $bin);
        return $array[1];
    }
}

class geovector
{

    const esp = 1e-10;

    function dcmp($x)
    {
        if (abs($x) < self::esp) {
            return 0;
        }
        return $x > 0 ? 1 : -1;
    }

    //向量乘积
    function dot($v1, $v2)
    {
        return ($v1[0] * $v2[0]) + ($v1[1] * $v2[1]);
    }

    //向量长度
    function length($v)
    {
        return sqrt($this->dot($v, $v));
    }

    //向量差积
    function cross($coorda, $coordb)
    {
        return ($coorda[0] * $coordb[1]) - ($coorda[1] * $coordb[0]);
    }

    function point_line_short_distance($coordp, $line)
    {
        $coorda = $line[0];
        $coordb = $line[1];
        $ab = [$coordb[0] - $coorda[0], $coordb[1] - $coorda[1]];
        $ap = [$coordp[0] - $coorda[0], $coordp[1] - $coorda[1]];
        $bp = [$coordp[0] - $coordb[0], $coordp[1] - $coordb[1]];

        if ($this->dot($ab, $ap) * $this->dot($ab, $bp) > 0) {
            $lpa = $this->length($ap);
            //$lpa = common::distance($coorda, $coordp);
            $lpb = $this->length($bp);
            // $lpa = common::distance($coordb, $coordp);
            return min($lpa, $lpb);
        }

        return abs($this->cross($bp, $ap)) / $this->length($ab);
    }

    function point_polygon_short_distance($coordp, $polyogon)
    {
        $distance = null;
        for ($i = 0; $i < count($polyogon) - 1; $i++) {
            $d = $this->point_line_short_distance($coordp, [$polyogon[$i], $polyogon[$i + 1]]);
            if ($distance === null || $distance > $d) {
                $distance = $d;
            }
        }

        return $distance * 100000;
    }

}


class UserToken
{

    static function expire_time($user_type)
    {
        if ($user_type == OPER_TYPE_CS || $user_type == OPER_TYPE_CC) {
            return 3600 * 3;
        } else {
            return 3600 * 24 * 30;
        }
    }

    function mgr_reg_token($id, $token)
    {
        $key = 'TOKENS|' . $id;

        $redis = Cache::store('redis')->handler();
        $redis->hSet($key, $token, date('Y-m-d H:i:s'));
    }

    function mgr_delete_token($id, $token)
    {
        $key = 'TOKENS|' . $id;
        $redis = Cache::store('redis')->handler();
        $redis->hDel($key, $token);
    }

    function mgr_delete_all_token($id)
    {
        $key = 'TOKENS|' . $id;
        $redis = Cache::store('redis')->handler();
        $tokens = $redis->hKeys($key);
        foreach ($tokens as $token) {
            $k = "TOKEN|" . $token;
            $redis->rm($k);
        }
        $redis->rm($key);
    }

    function update($token, $data)
    {
        $key = "TOKEN|" . $token;
        $redis = Cache::store('redis')->handler();
        $redis->hMset($key, $data);
    }

    function create_user_token($userinfo)
    {
        $paramlist = [
            'id' => 'string',
            'name' => 'string',
            'phone' => 'string',
            'channel' => 'string',
        ];

        $info = common::type_conver($userinfo, $paramlist);
        $info['user_type'] = OPER_TYPE_USER;
        $info['create_time'] = date('Y-m-d H:i:s');
        $token = common::sn();

        $key = "TOKEN|" . $token;

        $redis = Cache::store('redis')->handler();
        $redis->hMset($key, $info);
        $redis->expire($key, self::expire_time(OPER_TYPE_USER));
        return $token;
    }

    function create_cc_token($userinfo, $token = null)
    {
        $paramlist = [
            'id' => 'string',
            'name' => 'string',
            'phone' => 'string',
            'cc_role_id' => 'string',
            'city_id' => 'string',
            'acl' => 'string',
            'channel' => 'string',
        ];

        $info = common::type_conver($userinfo, $paramlist);
        $info['user_type'] = OPER_TYPE_CC;
        $info['channel'] = 'CC';
        $info['role_id'] = $userinfo['cc_role_id'];
        $info['create_time'] = date('Y-m-d H:i:s');
        if (empty($token)) {
            $token = common::sn();
        }

        $key = "TOKEN|" . $token;

        $redis = Cache::store('redis')->handler();
        $redis->hMset($key, $info);
        $redis->expire($key, self::expire_time(OPER_TYPE_CC));

        $this->mgr_reg_token($userinfo['id'], $token);
        return $token;
    }

    function create_cs_token($userinfo, $token = null)
    {
        $paramlist = [
            'id' => 'string',
            'name' => 'string',
            'phone' => 'string',
            'cc_role_id' => 'string',
            //  'city_id' => 'string',
            'acl' => 'string',
            'channel' => 'string',
        ];

        $info = common::type_conver($userinfo, $paramlist);
        $info['user_type'] = OPER_TYPE_CS;
        $info['role_id'] = $userinfo['cs_role_id'];
        $info['channel'] = 'CS';
        $info['create_time'] = date('Y-m-d H:i:s');

        if (empty($token)) {

        }
        $token = common::sn();

        $key = "TOKEN|" . $token;

        $redis = Cache::store('redis')->handler();
        $redis->hMset($key, $info);
        $redis->expire($key, self::expire_time(OPER_TYPE_CS));

        $this->mgr_reg_token($userinfo['id'], $token);
        return $token;
    }

    function create_ops_token($userinfo, $token = null)
    {
        $paramlist = [
            'id' => 'string',
            'name' => 'string',
            'phone' => 'string',
            'ops_role_id' => 'string',
            'city_id' => 'string',
            'acl' => 'string',
            'channel' => 'string',
        ];

        $info = common::type_conver($userinfo, $paramlist);
        $info['user_type'] = OPER_TYPE_OPS;
        $info['role_id'] = $userinfo['ops_role_id'];
        $info['channel'] = 'OPS';
        if (empty($token)) {
            $token = common::sn();
        }
        $info['create_time'] = date('Y-m-d H:i:s');

        $key = "TOKEN|" . $token;

        $redis = Cache::store('redis')->handler();
        $redis->hMset($key, $info);
        $redis->expire($key, self::expire_time(OPER_TYPE_OPS));

        $this->mgr_reg_token($userinfo['id'], $token);
        return $token;
    }

    function query_info($token)
    {
        $redis = Cache::store('redis')->handler();
        $key = "TOKEN|" . $token;
        $kvs = $redis->hGetAll($key);
        if (empty($kvs))
            return null;

        $paramlist = null;
        if (intval($kvs['user_type']) == OPER_TYPE_USER) {
            $paramlist = [
                'id' => 'string',
                'name' => 'string',
                'phone' => 'string',
                'user_type' => 'int',
                'channel' => 'string',
            ];
        } else if (intval($kvs['user_type']) == OPER_TYPE_CC) {
            $paramlist = [
                'id' => 'string',
                'name' => 'string',
                'phone' => 'string',
                'cc_role_id' => 'string',
                'role_id' => 'string',
                'city_id' => 'string',
                'acl' => 'array',
                'channel' => 'string',
                'user_type' => 'int',
            ];
        } else if (intval($kvs['user_type']) == OPER_TYPE_CS) {
            $paramlist = [
                'id' => 'string',
                'name' => 'string',
                'phone' => 'string',
                'cs_role_id' => 'string',
                'role_id' => 'string',
                // 'city_id' => 'string',
                'acl' => 'array',
                'channel' => 'string',
                'user_type' => 'int',
            ];
        } else if (intval($kvs['user_type']) == OPER_TYPE_OPS) {
            $paramlist = [
                'id' => 'string',
                'name' => 'string',
                'phone' => 'string',
                'ops_role_id' => 'string',
                'role_id' => 'string',
                'city_id' => 'string',
                'acl' => 'array',
                'channel' => 'string',
                'user_type' => 'int',
            ];
        } else {
            return [];
        }

        $r = rand(0, 20);
        if ($r == 0) {
            $redis->expire($key, self::expire_time($kvs['user_type']));
        }
        return common::type_conver($kvs, $paramlist);
    }

    function delete($id, $token)
    {
        $key = "TOKEN|" . $token;
        $redis = Cache::store('redis')->handler();
        $redis->rm($key);
        $this->mgr_delete_token($id, $token);
    }

}
